package com.quanxiaoha.weblog.admin.service.Impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.quanxiaoha.weblog.admin.convert.ArticleDetailConvert;
import com.quanxiaoha.weblog.admin.model.vo.article.ReqVO.DeleteArticleReqVO;
import com.quanxiaoha.weblog.admin.model.vo.article.ReqVO.FindArticlePageListReqVO;
import com.quanxiaoha.weblog.admin.model.vo.article.ReqVO.PublishArticleReqVO;
import com.quanxiaoha.weblog.admin.model.vo.article.ReqVO.UpdateArticleReqVO;
import com.quanxiaoha.weblog.admin.model.vo.article.RspVO.FindArticleDetailRspVO;
import com.quanxiaoha.weblog.admin.model.vo.article.RspVO.FindArticlePageListRspVO;
import com.quanxiaoha.weblog.admin.service.AdminArticleService;
import com.quanxiaoha.weblog.common.domain.dos.*;
import com.quanxiaoha.weblog.common.domain.mapper.*;
import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum;
import com.quanxiaoha.weblog.common.exception.BizException;
import com.quanxiaoha.weblog.common.utils.PageResponse;
import com.quanxiaoha.weblog.common.utils.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.ObjectStreamException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdminArticleServiceImpl implements AdminArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleContentMapper articleContentMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response saveArticle(PublishArticleReqVO publishArticleReqVO) {
        //vo转文章DO
        ArticleDO articleDO = ArticleDO.builder()
                .summary(publishArticleReqVO.getSummary())
                .cover(publishArticleReqVO.getCover())
                .title(publishArticleReqVO.getTitle())
                .updateTime(LocalDateTime.now())
                .createTime(LocalDateTime.now())
                .build();

        //保存文章到数据库
        //获取到发布文章后的文章ID
        articleMapper.insert(articleDO);

        Long articleId = articleDO.getId();

        //VO转文章详情DO
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .articleId(articleId)
                .content(publishArticleReqVO.getContent())
                .build();
        //新增文章详情记录
        articleContentMapper.insert(articleContentDO);

        //新增文章-分类关联记录
        //a.查看是否存在其分类
        Long categoryId = publishArticleReqVO.getCategoryId();
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);

        //b.不存在其分类，返回异常信息
        if (Objects.isNull(categoryDO)) {
            log.warn("发布的文章分类不存在；{}", publishArticleReqVO);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        //c.若存在其分类，则插入文章-分类关联记录
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .articleId(articleId)
                .categoryId(categoryId)
                .build();

        articleCategoryRelMapper.insert(articleCategoryRelDO);


        //新增文章-标签关联记录

        insertTagsRels(articleId, publishArticleReqVO.getTags());


        return Response.success();
    }


    /**
     * 删除文章，需要操作文章表、文章详情表、文章-标签记录表、文章-分类记录表
     * 开启事务确保数据库操作的原子性
     *
     * @param deleteArticleReqVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response delArticle(DeleteArticleReqVO deleteArticleReqVO) {
        Long articleId = deleteArticleReqVO.getId();
        articleMapper.deleteById(articleId);
        articleContentMapper.delByArticleId(articleId);
        articleTagRelMapper.delByArticleId(articleId);
        articleCategoryRelMapper.delByArticleId(articleId);
        return Response.success();
//        return i1 + i2 + i3 + i4 == 4 ? Response.success() : Response.fail();
    }


    /**
     * 文章分页接口
     *
     * @param findArticlePageListReqVO
     * @return
     */
    @Override
    public Response getArticlePageList(FindArticlePageListReqVO findArticlePageListReqVO) {
        //1.获得分页条件
        Long size = findArticlePageListReqVO.getSize();
        Long current = findArticlePageListReqVO.getCurrent();
        String title = findArticlePageListReqVO.getTitle();
        LocalDate startDate = findArticlePageListReqVO.getStartDate();
        LocalDate endDate = findArticlePageListReqVO.getEndDate();
        //2.执行分页查询
        Page<ArticleDO> articleDOPage = articleMapper.selectPageList(size, current, title, startDate, endDate);
        List<ArticleDO> articleDOS = articleDOPage.getRecords();

        //3.do转vo
        List<FindArticlePageListRspVO> vos = null;
        if (!CollectionUtils.isEmpty(articleDOS)) {
            vos = articleDOS.stream()
                    .map(articleDO -> FindArticlePageListRspVO.builder()
                            .title(articleDO.getTitle())
                            .createTime(articleDO.getCreateTime())
                            .cover(articleDO.getCover())
                            .id(articleDO.getId())
                            .build()
                    ).collect(Collectors.toList());
        }

        return PageResponse.success(articleDOPage, vos);
    }


    /**
     * 根据文章id查询文章详情页
     *
     * @param id
     * @return
     */
    @Override
    public Response getArticleDetailById(Long id) {
        //判断文章是否存在
        ArticleDO articleDO = articleMapper.selectById(id);
        if (Objects.isNull(articleDO)) {
            //不存在抛出业务异常
            log.warn("==> 查询的文章id不存在：{}", id);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_EXISTED);

        }
        //根据文章id查询出文章内容、文章分类id、文章标签id集合
        ArticleContentDO articleContentDO = articleContentMapper.getByArticleId(id);
        String content = articleContentDO.getContent();
        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.getByArticleId(id);
        Long categoryId = articleCategoryRelDO.getCategoryId();
        List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.getByArticleId(id);
        List<Long> tagIds = articleTagRelDOS.stream()
                .map(articleTagRelDO -> articleTagRelDO.getTagId())
                .collect(Collectors.toList());

        //DO转VO
        FindArticleDetailRspVO rspVO = ArticleDetailConvert.INSTANCE.convertDO2VO(articleDO);
        rspVO.setContent(content);
        rspVO.setCategoryId(categoryId);
        rspVO.setTagIds(tagIds);

        return Response.success(rspVO);

    }

    @Override
    public Response updateArticleById(UpdateArticleReqVO updateArticleReqVO) {
        //1.vo转articledo，并更新
        Long articleId = updateArticleReqVO.getId();
        ArticleDO articleDO = ArticleDO.builder()
                .title(updateArticleReqVO.getTitle())
                .cover(updateArticleReqVO.getCover())
                .summary(updateArticleReqVO.getSummary())
                .id(articleId)
                .updateTime(LocalDateTime.now())
                .build();
        int count = articleMapper.updateById(articleDO);
        //2.检验更新结果，判断文章是否存在
        if (count == 0) {
            log.warn("文章不存在：{}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_EXISTED);
        }

        //3.vo转articleContentDO,并更新
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .articleId(articleId)
                .content(updateArticleReqVO.getContent())
                .build();
        articleContentMapper.updateByArticleId(articleContentDO);

        //4.更新其分类
        //4.1校验分类是否存在
        Long categoryId = updateArticleReqVO.getCategoryId();
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDO)) {
            log.warn("该分类不存在：{}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        //4.2分类若存在，则先删除文章-分类关联记录
        articleCategoryRelMapper.delByArticleId(categoryId);
        //4.3插入一条新的文章-分类关联记录
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .categoryId(categoryId)
                .articleId(articleId)
                .build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);

        //5.更新其标签
        //5.1删除其标签与文章关联记录
        articleTagRelMapper.delByArticleId(articleId);
        //5.2重新插入文章-标签关联记录
        List<String> tags = updateArticleReqVO.getTags();
        insertTagsRels(articleId, tags);
        return Response.success();
    }

    private void insertTagsRels(Long articleId, List<String> tags) {
        //定义两个集合，NotExistTags存放不存在的标签，ExistTags存放已经存在的标签
        List<String> notExistTags = null;
        List<String> existTags = null;

        //数据库中查询标签，判断标签是否为空
        List<TagDO> tagDOS = tagMapper.selectList(null);
        //将数据库中转为标签名的集合
        List<String> tagDONames = tagDOS.stream()
                .map(tagDO -> tagDO.getName()).
                collect(Collectors.toList());

        //为空则将入参的标签存入notExistTags中


        if (CollectionUtils.isEmpty(tagDOS)) {
            notExistTags = tags;
        }

        //不为空则根据标签id将标签分别存入到两个集合当中

        //以标签字符串为依据，判断其中字符串是否存在，不存在过滤到notExistTags，否则赋给existTags
        existTags = tags.stream().filter(tag -> tagDONames.contains(tag))
                .collect(Collectors.toList());
        notExistTags = tags.stream().filter(tag -> !tagDONames.contains(tag))
                .collect(Collectors.toList());


        //若用户提交了一小写的标签java,需要转换为Java去在和数据库中比较之后，再进行存储
        //将tagDONames转为全部为标签小写的集合
        List<String> tagLowerNames = tagDONames.stream()
                .map(tagDOName -> tagDOName.toLowerCase())
                .collect(Collectors.toList());
        // 1.用迭代器做删除操作
        Iterator<String> iterator = notExistTags.iterator();
        while (iterator.hasNext()) {
            //遍历到每一个不存在的tag
            String notExistTag = iterator.next();
            //全部去小写后判断是否在TagDONames中存在
            if (tagLowerNames.contains(notExistTag.toLowerCase())) {
                //如果存在,从原来的不存在列表移动到已存在列表
                iterator.remove();
                existTags.add(notExistTag);
            }
        }


        //由于需要把已经存在的标签，在对应的数据库中查询到的标签id找到,则需要将Id全部提取出来
        //先提取出已经存在于数据库中的标签对应的id
        List<Long> Ids = existTags.stream().map(
                existTag -> {
                    Long id = null;
                    for (TagDO tagDO : tagDOS) {
                        if (tagDO.getName().toLowerCase().equals(existTag)) {
                            id = tagDO.getId();
                        }
                    }
                    return id;
                }
        ).collect(Collectors.toList());

        //未存在的标签insert在数据库中后，需要拿到其ID
        if (!CollectionUtils.isEmpty(notExistTags)) {
            for (String notExistTag : notExistTags) {
                TagDO tagDO = TagDO.builder()
                        .name(notExistTag)
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .build();
                //拿到新插入的标签的id
                tagMapper.insert(tagDO);
                //添加到id集合当中
                Ids.add(tagDO.getId());
            }
        }
        //在将两个集合的标签信息+发布的文章信息插入到文章-标签关联表中
        List<ArticleTagRelDO> articleTagRelDOS = Ids.stream().map(
                id -> ArticleTagRelDO.builder()
                        .tagId(id)
                        .articleId(articleId)
                        .build()
        ).collect(Collectors.toList());

        //再调用自定义mp的方法
        // 批量插入
        articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);

    }
}
